package backboneCode;

import java.util.List;

import org.eclipse.uml2.uml.Class;

public class RuleValidator {
	
	public void ValidateElement(org.eclipse.uml2.uml.Element element) {
		
		String elementName = ((org.eclipse.uml2.uml.NamedElement)element).getName();
		List<RuleSet> ruleSetList = GlobalStorage.getSystemRuleSet(elementName);
		if (ruleSetList == null)
			return;
			
		for (RuleSet ruleSet : ruleSetList) {
			if (ruleSet.GetTypeOfRuleSet().compareToIgnoreCase("PackageCompositeRuleSet") == 0) {
				ValidatePackageComposite(ruleSet, (org.eclipse.uml2.uml.Package) element);
			} else if (ruleSet.GetTypeOfRuleSet().compareToIgnoreCase("AssociationRuleSet") == 0) {
				ValidateAssociation(ruleSet, (org.eclipse.uml2.uml.Class) element);
			} else if (ruleSet.GetTypeOfRuleSet().compareToIgnoreCase("RelationshipRuleSet") == 0) {
				ValidateRelationship(ruleSet, (org.eclipse.uml2.uml.Namespace) element);
			} else if (ruleSet.GetTypeOfRuleSet().compareToIgnoreCase("AttributeRuleSet") == 0) {
				ValidateAttribute(ruleSet, (org.eclipse.uml2.uml.Class) element);
			}
			
		}
	}
	
	private void ValidateAttribute(RuleSet ruleSet, org.eclipse.uml2.uml.Class umlClass) {
		List<Rules> ruleList = ruleSet.GetRuleSet();
		boolean OUEDR = GlobalStorage.OnlyUseExplicitDefinedRules;
		
		if (ruleList ==  null) {
			return;
		}
		
		for (org.eclipse.uml2.uml.Property attribute : umlClass.getAllAttributes()) {
			if (attribute.getAssociation() instanceof org.eclipse.uml2.uml.Association)
				continue;
			
			
		}
	}

	private void ValidateRelationship(RuleSet ruleSet, org.eclipse.uml2.uml.Namespace umlNamespace) {
		List<Rules> ruleList = ruleSet.GetRuleSet();
		boolean OUEDR = GlobalStorage.OnlyUseExplicitDefinedRules;
		
		if (ruleList ==  null) {
			return;
		}
		
		for (org.eclipse.uml2.uml.Relationship relationship : umlNamespace.getRelationships()) {
			boolean found = false;
			String relationshipType = "";
			for (Rules rule : ruleList) {
				if (relationship instanceof org.eclipse.uml2.uml.Realization)
					relationshipType = "Realization";
				else if (relationship instanceof org.eclipse.uml2.uml.Dependency)
					relationshipType = "Dependency";
				else if (relationship instanceof org.eclipse.uml2.uml.Generalization)
					relationshipType = "Generalization";

				org.eclipse.uml2.uml.Namespace otherEnd = null;
				for (org.eclipse.uml2.uml.Element endElement : relationship.getRelatedElements()) {
					if (((org.eclipse.uml2.uml.Namespace) endElement).getName().compareToIgnoreCase(umlNamespace.getName()) != 0) {
						otherEnd = (org.eclipse.uml2.uml.Namespace) endElement;
					}
				}
				
				rule.Name = umlNamespace.getName();
				for (org.eclipse.uml2.uml.Stereotype stereotype : umlNamespace.getAppliedStereotypes()) {
					for (org.eclipse.uml2.uml.Stereotype otherEndStereotype : otherEnd.getAppliedStereotypes()) {
						if (rule.Stereotype.compareToIgnoreCase(stereotype.getName()) == 0 &&
								rule.ContainerName.compareToIgnoreCase(relationshipType) == 0 && 
								rule.ContainerStereotype.compareToIgnoreCase(otherEndStereotype.getName()) == 0) {
							rule.NumberOfElements++;
							rule.checked = true;
							found = true;
							if (relationshipType.compareTo("Generalization") != 0)
								if (rule.Visibility.compareTo(((org.eclipse.uml2.uml.NamedElement)relationship).getVisibility().getName()) != 0)
									rule.correctVisibility = false;
						}
					}
				}
			}	
			if (!found && !OUEDR && relationshipType.compareTo("") != 0) {
				MissingRelationshipRule(relationship, relationshipType, umlNamespace);
			}
		}
		
	}

	private void MissingRelationshipRule(org.eclipse.uml2.uml.Relationship relationship,
			String relationshipType, org.eclipse.uml2.uml.Namespace umlNamespace) {
		
		Rules rule = null;
		
		String Name = umlNamespace.getName();
		String OtherName = "";
		
		org.eclipse.uml2.uml.Namespace otherEnd = null;
		for (org.eclipse.uml2.uml.Element endElement : relationship.getRelatedElements()) {
			if (((org.eclipse.uml2.uml.Namespace) endElement).getName().compareToIgnoreCase(Name) != 0) {
				otherEnd = (org.eclipse.uml2.uml.Namespace) endElement;
			}
		}
		
		OtherName = otherEnd.getName();		
		rule = new Rules(Name, OtherName, relationshipType, 2, 2, "");
		rule.Name = "MissingRelationship";
		if (GlobalStorage.ContainRule(rule)) {
			rule = GlobalStorage.getNotDefinedRule(rule);
			rule.NumberOfElements++;
		} else {
			rule.NumberOfElements++;
			GlobalStorage.addToNotDefinedRules(rule);
		}
	}

	private void ValidateAssociation(RuleSet ruleSet, org.eclipse.uml2.uml.Class umlClass) {
		List<Rules> ruleList = ruleSet.GetRuleSet();
		boolean OUEDR = GlobalStorage.OnlyUseExplicitDefinedRules;
		
		if (ruleList ==  null) {
			return;
		}
		for (org.eclipse.uml2.uml.Stereotype stereotype : umlClass.getAppliedStereotypes()) {
			String stereotypeNameString = stereotype.getName();
			for (org.eclipse.uml2.uml.Association association : umlClass.getAssociations()) {
				boolean found = false;
				for (org.eclipse.uml2.uml.Property prop : association.getMemberEnds()) {
					boolean tempFound = false; 
					//Check to see if it's same end as the umlClass
					for (org.eclipse.uml2.uml.Stereotype tempStereotype : ((org.eclipse.uml2.uml.Class)prop.getType()).getAppliedStereotypes()) {
						if (tempStereotype.getName().compareToIgnoreCase(stereotypeNameString) == 0)
							tempFound = true;
					}
					if (!tempFound)
						continue;
					for (org.eclipse.uml2.uml.Stereotype otherEnd : 
						((org.eclipse.uml2.uml.Class)prop.getOtherEnd().getType()).getAppliedStereotypes()) {
						for (Rules rule : ruleList) {
							if (rule.Name.compareToIgnoreCase(stereotypeNameString) == 0 &&
									rule.Aggregation.compareToIgnoreCase(prop.getAggregation().getName()) == 0 &&
									(rule.Navigable == prop.isNavigable()) &&
									rule.ContainerName.compareTo(otherEnd.getName()) == 0) {
								found = true;
								rule.checked = true;
								rule.NumberOfElements++;
								rule.Stereotype = ((org.eclipse.uml2.uml.Class)prop.getType()).getName();
								rule.ContainerStereotype = ((org.eclipse.uml2.uml.Class)prop.getOtherEnd().getType()).getName();
								if (rule.Visibility.compareTo(association.getVisibility().getName()) != 0)
									rule.correctVisibility = false;
							}
							if (rule.Name.compareToIgnoreCase(stereotypeNameString) == 0 &&
									rule.ContainerName.compareTo(otherEnd.getName()) == 0 &&
									rule.Aggregation.compareToIgnoreCase(prop.getAggregation().getName()) != 0) {
								found = true;
								rule.correctAggregation = false;
								rule.Stereotype = ((org.eclipse.uml2.uml.Class)prop.getType()).getName();
								rule.ContainerStereotype = ((org.eclipse.uml2.uml.Class)prop.getOtherEnd().getType()).getName();
								if (rule.Visibility.compareTo(association.getVisibility().getName()) != 0)
									rule.correctVisibility = false;
							} else if (rule.Name.compareToIgnoreCase(stereotypeNameString) == 0 &&
											rule.ContainerName.compareTo(otherEnd.getName()) == 0 &&
											rule.Navigable != prop.isNavigable()) {
								found = true;
								rule.correctNavigability = false;
								rule.Stereotype = ((org.eclipse.uml2.uml.Class)prop.getType()).getName();
								rule.ContainerStereotype = ((org.eclipse.uml2.uml.Class)prop.getOtherEnd().getType()).getName();
								if (rule.Visibility.compareTo(association.getVisibility().getName()) != 0)
									rule.correctVisibility = false;
							}
						}
					}
					if (!found && !OUEDR) {
						MissingAssociationRule(prop);
					}
				}
			}
		}
	}

	private void MissingAssociationRule(org.eclipse.uml2.uml.Property prop) {
		Rules rule = null;
		
		rule = new Rules(prop.getName(), 1, 1, prop.getOtherEnd().getName(), false, "MissingAssociationRule", "");
		rule.Stereotype = ((org.eclipse.uml2.uml.Class)prop.getType()).getName();
		rule.ContainerStereotype = ((org.eclipse.uml2.uml.Class)prop.getOtherEnd().getType()).getName();
		if (GlobalStorage.ContainRule(rule)) {
			rule = GlobalStorage.getNotDefinedRule(rule);
			rule.NumberOfElements++;
		} else {
			rule.NumberOfElements++;
			GlobalStorage.addToNotDefinedRules(rule);
		}
	}

	private void ValidatePackageComposite(RuleSet ruleSet, org.eclipse.uml2.uml.Package umlPackage) {
		List<Rules> ruleList = ruleSet.GetRuleSet();
		boolean OUEDR = GlobalStorage.OnlyUseExplicitDefinedRules;
		
		if (ruleList ==  null) {
			return;
		}
		for (org.eclipse.uml2.uml.Element element : umlPackage.getOwnedElements()) {
			boolean found = false;
			for (org.eclipse.uml2.uml.Stereotype stereotype : element.getAppliedStereotypes()) {
				found = false;
				for (Rules rule : ruleList) {
					if (rule.Name.compareToIgnoreCase(stereotype.getName()) == 0) {
						rule.NumberOfElements++;
						rule.checked = true;
						found = true;
						if (rule.Visibility.compareTo(((org.eclipse.uml2.uml.NamedElement)element).getVisibility().getName()) != 0)
							rule.correctVisibility = false;
					}
				}
				if (!found && !OUEDR) {
					MissingPackageCompositeRule(element, umlPackage, stereotype);											
					found = true;
				}
			}
			if (!found && !OUEDR) {
				MissingPackageCompositeRule(element, umlPackage, null);
			}
		}
	}

	private void MissingPackageCompositeRule(org.eclipse.uml2.uml.Element element, org.eclipse.uml2.uml.Package umlPackage,
			org.eclipse.uml2.uml.Stereotype stereotype) {
		Rules rule = null;
		if (GetType(element).compareTo("Unknown") == 0) {
			//TODO:
			//System.out.println("Found unknown element!");
			return;
		}
		
		if (GetType(element).compareTo("Class") == 0)
			rule = new Rules(((org.eclipse.uml2.uml.Class)element).getName(), "MissingPackageCompositeRule", 0, 0, umlPackage.getName(), GetType(element), "");
		else if (GetType(element).compareTo("Package") == 0)
			rule = new Rules(((org.eclipse.uml2.uml.Package)element).getName(), "MissingPackageCompositeRule", 0, 0, umlPackage.getName(), GetType(element), "");
		
		if (GlobalStorage.ContainRule(rule)) {
			rule = GlobalStorage.getNotDefinedRule(rule);
			rule.NumberOfElements++;
		} else {
			rule.NumberOfElements++;
			GlobalStorage.addToNotDefinedRules(rule);
		}	
	}
	
	public static String GetType(org.eclipse.uml2.uml.Element element) {
		
		if (element instanceof org.eclipse.uml2.uml.Package)
			return "Package";
		else if (element instanceof org.eclipse.uml2.uml.Class)
			return "Class";
		
		return "Unknown";
	}
}
